package br.com.harley.torneio.util;

import static org.junit.Assert.fail;

import org.easymock.EasyMock;
import org.junit.Before;
import org.powermock.api.easymock.PowerMock;
import org.slf4j.Logger;

import br.com.harley.torneio.business.IBaseBC;
import br.gov.frameworkdemoiselle.internal.producer.LoggerProducer;
import br.gov.frameworkdemoiselle.template.DelegateCrud;
import br.gov.frameworkdemoiselle.template.JPACrud;

/**
 * Classe base para os testes da camada de negócio.
 * 
 * @param <T>
 *            Classe a ser testada.
 * @param <K>
 *            Classe do DAO.
 */
@SuppressWarnings("unchecked")
public abstract class BaseTest<T extends DelegateCrud, K extends JPACrud> {

	private IBaseBC objetoTestado;

	private Object daoMockado;

	@Before
	public void setUp() {

		if (this.getClass().isAnnotationPresent(ClasseTestada.class)) {

			ClasseTestada anotacaoClasseTestada = this.getClass().getAnnotation(ClasseTestada.class);
			Class classeTestada = anotacaoClasseTestada.classeTestada();

			Class classDaoMockado = anotacaoClasseTestada.daoMockado();
			daoMockado = EasyMock.createMock(classDaoMockado);

			objetoTestado = (IBaseBC) PowerMock.createPartialMock(classeTestada, "getDelegate");
			Logger logger = LoggerProducer.create(classeTestada);
			objetoTestado.setLogger(logger);
			try {
				PowerMock.expectPrivate(objetoTestado, "getDelegate").andReturn(daoMockado).anyTimes();
				EasyMock.replay(objetoTestado);
			} catch (Exception e) {
				fail("Falhou");
			}
		}
	}

	/**
	 * Obtém o objeto a ser testado com o método getDelegate mockado.
	 * 
	 * @return objetoTestado
	 */
	protected T getObjetoTestado() {
		return (T) objetoTestado;
	}

	/**
	 * Seta o objeto a ser testado, caso seja necessario.
	 * 
	 * @param objetoTestado
	 *            objeto a ser testado pela classe.
	 */
	public void setObjetoTestado(IBaseBC objetoTestado) {
		this.objetoTestado = objetoTestado;
	}

	/**
	 * Obtém o DAO mockado.
	 * 
	 * @return DAO mockado
	 */
	protected K getDaoMockado() {
		return (K) daoMockado;
	}

	/**
	 * Seta o objeto a ser mocado, caso seja necessario.
	 * 
	 * @param daoMockad
	 *            oobjeto a ser mocado.
	 */
	public void setDaoMockado(Object daoMockado) {
		this.daoMockado = daoMockado;
	}

	/**
	 * Efetua o treinamento do método insert do DAO.
	 * 
	 * @param Boolean
	 *            que indica se é necessário efetuar replay no mock do DAO.
	 */
	protected void treinaDaoInsert(Boolean efetuaReplay) {
		((K) daoMockado).insert(EasyMock.anyObject());
		EasyMock.expectLastCall().anyTimes();
		efetuaReplayDAO(efetuaReplay);
	}

	/**
	 * Efetua o treinamento do método update do DAO.
	 * 
	 * @param Boolean
	 *            que indica se é necessário efetuar replay no mock do DAO.
	 */
	protected void treinaDaoUpdate(Boolean efetuaReplay) {
		((K) daoMockado).update(EasyMock.anyObject());
		EasyMock.expectLastCall().anyTimes();
		efetuaReplayDAO(efetuaReplay);
	}

	/**
	 * Efetua o treinamento do método delete do DAO.
	 * 
	 * @param Boolean
	 *            que indica se é necessário efetuar replay no mock do DAO.
	 */
	protected void treinaDaoDelete(Boolean efetuaReplay) {
		((K) daoMockado).delete(EasyMock.anyObject());
		EasyMock.expectLastCall().anyTimes();
		efetuaReplayDAO(efetuaReplay);
	}

	/**
	 * Método que verifica se será necessário efetuar replay no DAO.
	 * 
	 * @param efetuaReplay
	 *            {@link Boolean}
	 */
	protected void efetuaReplayDAO(Boolean efetuaReplay) {
		if (efetuaReplay) {
			EasyMock.replay(getDaoMockado());
		}
	}

}
